package net.za.cair.dip.transform;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

import net.za.cair.dip.model.MaterializationRank;
import net.za.cair.dip.model.MaterializationRanking;
import net.za.cair.dip.model.OntologyStructure;
import net.za.cair.dip.model.Rank;
import net.za.cair.dip.model.Ranking;
import net.za.cair.dip.util.AxiomManipulator;

import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLException;
import org.semanticweb.owlapi.reasoner.OWLReasonerFactory;

/*
 * Copyright (C) 2011, Centre for Artificial Intelligence Research
 *
 * Modifications to the initial code base are copyright of their
 * respective authors, or their employers as appropriate.  Authorship
 * of the modifications may be determined from the ChangeLog placed at
 * the end of this file.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.

 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 */

/**
 * Author: Kody Moodley<br>
 * Centre for Artificial Intelligence Research<br>
 * UKZN and CSIR<br>
 * Date: 10-Oct-2011<br><br>
 */

public class LexicographicRankingAlgorithm{
	
	private OWLReasonerFactory reasonerFactory;
	private OntologyStructure ontology;
	
	private Ranking rankingTmp;
	private MaterializationRanking ranking;
	private ArrayList<ArrayList<OWLAxiom>> conjunctionClauses;
	
	public LexicographicRankingAlgorithm(OWLReasonerFactory reasonerFactory, OntologyStructure ontologyStructure){
		this.reasonerFactory = reasonerFactory;
		this.ontology = ontologyStructure;
		rankingTmp = new Ranking();
	}
	
	public MaterializationRanking computeRanking() throws OWLException {
		RationalRankingAlgorithm protoRank = new RationalRankingAlgorithm(reasonerFactory, ontology);
		protoRank.computeRanking();
		rankingTmp = protoRank.getDisplayableRanking();
		rankingTmp = getPresumptiveConversion(rankingTmp);
		getMaterializationConversion(rankingTmp);
		return ranking;
	}

	private ArrayList<OWLAxiom> getLexicographicRank(ArrayList<OWLAxiom> rankAxioms){
		ArrayList<OWLAxiom> presumptiveRankAxioms = new ArrayList<OWLAxiom>();
		boolean done = false;
		int conjunctionClauseSize = rankAxioms.size();
		while (!done){
			conjunctionClauses = new ArrayList<ArrayList<OWLAxiom>>();

			choose(rankAxioms, conjunctionClauseSize);
			ArrayList<OWLAxiom> ors = new ArrayList<OWLAxiom>();
			for (ArrayList<OWLAxiom> tmp: removeDuplicates(conjunctionClauses)){
				OWLAxiom t = AxiomManipulator.getANDAxiomCombiner(tmp);
				ors.add(t);
			}
			presumptiveRankAxioms.add(AxiomManipulator.getORAxiomCombiner(ors));
			conjunctionClauseSize--;
			if (conjunctionClauseSize == 0)
				done = true;
		}
		
		//Reverse order
		ArrayList<OWLAxiom> reversed = new ArrayList<OWLAxiom>();
		int num = presumptiveRankAxioms.size();
		for (int i = num-1;i >= 0;i--){
			reversed.add(presumptiveRankAxioms.get(i));
		}
		return reversed;
	}
	
	public Ranking getPresumptiveConversion(Ranking ranking){
		Ranking result = new Ranking();
		result.add(ranking.get(0), 0);
		for (int i = 1; i < ranking.size();i++){
			ArrayList<OWLAxiom> tmp = ranking.get(i).getAxioms();
			ArrayList<OWLAxiom> pres = getLexicographicRank(tmp);
			for (OWLAxiom axiom: pres){
				ArrayList<OWLAxiom> rankAx = new ArrayList<OWLAxiom>();
				rankAx.add(axiom);
				result.add(new Rank(rankAx));
			}	
		}
		return result;
	}
	
	private void getMaterializationConversion(Ranking rankingPar){
		ranking = new MaterializationRanking();
		for (Rank rank: rankingPar.getRanking()){
			MaterializationRank tmp = rank.materialConversion();
			if (tmp.size() > 0)
				ranking.add(tmp);
		}
	}
	
	private ArrayList<ArrayList<OWLAxiom>> removeDuplicates(ArrayList<ArrayList<OWLAxiom>> set){
		ArrayList<ArrayList<OWLAxiom>> result = new ArrayList<ArrayList<OWLAxiom>>();
		Set<Set<OWLAxiom>> axiomSet = new HashSet<Set<OWLAxiom>>();
		for (ArrayList<OWLAxiom> tmp: set){
			Set<OWLAxiom> tmpSet = new HashSet<OWLAxiom>();
			for (OWLAxiom tmp2: tmp){
				tmpSet.add(tmp2);
			}
			axiomSet.add(tmpSet);
		}
		
		for (Set<OWLAxiom> tmp: axiomSet){
			ArrayList<OWLAxiom> tmpSet = new ArrayList<OWLAxiom>();
			for (OWLAxiom tmp2: tmp){
				tmpSet.add(tmp2);
			}
			result.add(tmpSet);
		}
							
		return result;
	}
	
	private void choose(ArrayList<OWLAxiom> a, int R) { enumerate(a, a.size(), R); }

    private void enumerate(ArrayList<OWLAxiom> a, int n, int r) {
        if (r == 0) {
        	ArrayList<OWLAxiom> tmp = new ArrayList<OWLAxiom>();
            for (int i = n; i < a.size(); i++){
            	tmp.add(a.get(i));
            }
        	conjunctionClauses.add(tmp);
            return;
        }
        for (int i = 0; i < n; i++) {
            swap(a, i, n-1);
            enumerate(a, n-1, r-1);
            swap(a, i, n-1);
         }
    }  

    private static void swap(ArrayList<OWLAxiom> a, int i, int j) {
    	OWLAxiom tmp = a.get(i);
    	OWLAxiom tmp2 = a.get(j);
    	a.set(j, tmp);
    	a.set(i, tmp2);
    }
	
    public Ranking getDisplayableRanking(){
		return rankingTmp;
	}
}